<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use Carbon\Carbon;

class DatabaseBackupCommand extends Command
{
    /**
     * 命令名称
     *
     * @var string
     */
    protected $signature = 'db:backup {--email= : 指定接收备份文件的邮箱地址}';

    /**
     * 命令描述
     *
     * @var string
     */
    protected $description = '备份数据库并发送到指定邮箱';

    /**
     * 执行命令
     */
    public function handle()
    {
        $this->info('开始执行数据库备份任务...');
        $startTime = Carbon::now();
        $this->info('开始时间: ' . $startTime->toDateTimeString());

        try {
            // 生成备份文件名
            $backupFileName = 'money_record_backup_' . $startTime->format('Y_m_d_H_i_s') . '.sql';
            $backupPath = storage_path('app/backups/' . $backupFileName);

            // 确保备份目录存在
            if (!file_exists(dirname($backupPath))) {
                mkdir(dirname($backupPath), 0755, true);
            }

            // 获取数据库配置
            $dbConfig = config('database.connections.' . config('database.default'));
            $host = $dbConfig['host'];
            $port = $dbConfig['port'];
            $database = $dbConfig['database'];
            $username = $dbConfig['username'];
            $password = $dbConfig['password'];

            // 构建mysqldump命令
            $command = sprintf(
                'mysqldump --host=%s --port=%s --user=%s --password=%s --single-transaction --routines --triggers %s > %s',
                escapeshellarg($host),
                escapeshellarg($port),
                escapeshellarg($username),
                escapeshellarg($password),
                escapeshellarg($database),
                escapeshellarg($backupPath)
            );

            $this->info('正在备份数据库...');

            // 执行备份命令
            $output = [];
            $returnCode = 0;
            exec($command . ' 2>&1', $output, $returnCode);

            if ($returnCode !== 0) {
                throw new \Exception('数据库备份失败，返回码: ' . $returnCode);
            }

            // 检查备份文件是否生成成功
            if (!file_exists($backupPath) || filesize($backupPath) === 0) {
                throw new \Exception('备份文件生成失败或文件为空');
            }

            $fileSize = round(filesize($backupPath) / 1024 / 1024, 2); // MB
            $this->info("备份完成！文件大小: {$fileSize} MB");

            // 发送邮件
            $this->sendBackupEmail($backupPath, $backupFileName, $fileSize, $startTime);

            // 清理旧的备份文件（保留最近7天的备份）
            $this->cleanOldBackups();

            $endTime = Carbon::now();
            $duration = $startTime->diffInSeconds($endTime);
            $this->info("备份任务完成！总耗时: {$duration} 秒");

            Log::info('数据库备份任务执行成功', [
                'file' => $backupFileName,
                'size' => $fileSize . ' MB',
                'duration' => $duration . ' 秒'
            ]);

        } catch (\Exception $e) {
            $this->error('备份任务执行失败: ' . $e->getMessage());
            Log::error('数据库备份任务执行失败', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);

            // 发送错误通知邮件
            $this->sendErrorEmail($e->getMessage(), $startTime);
        }
    }

    /**
     * 发送备份文件到邮箱
     */
    private function sendBackupEmail($filePath, $fileName, $fileSize, $startTime)
    {
        try {
            $recipientEmail = $this->option('email') ?: config('mail.backup_recipient', config('mail.from.address'));

            if (!$recipientEmail) {
                throw new \Exception('未配置接收备份文件的邮箱地址');
            }

            $this->info("正在发送备份文件到邮箱: {$recipientEmail}");

            Mail::send('emails.database-backup', [
                'fileName' => $fileName,
                'fileSize' => $fileSize,
                'backupTime' => $startTime->format('Y-m-d H:i:s'),
                'appName' => config('app.name', '记账助手')
            ], function ($message) use ($recipientEmail, $fileName, $filePath) {
                $message->to($recipientEmail)
                        ->subject('数据库备份 - ' . config('app.name', '记账助手') . ' - ' . date('Y-m-d'))
                        ->attach($filePath, [
                            'as' => $fileName,
                            'mime' => 'application/sql'
                        ]);
            });

            $this->info('备份文件已成功发送到邮箱');

        } catch (\Exception $e) {
            $this->error('邮件发送失败: ' . $e->getMessage());
            Log::error('备份邮件发送失败', ['error' => $e->getMessage()]);
            throw $e;
        }
    }

    /**
     * 发送错误通知邮件
     */
    private function sendErrorEmail($errorMessage, $startTime)
    {
        try {
            $recipientEmail = $this->option('email') ?: config('mail.backup_recipient', config('mail.from.address'));

            if (!$recipientEmail) {
                return;
            }

            Mail::send('emails.backup-error', [
                'errorMessage' => $errorMessage,
                'failTime' => $startTime->format('Y-m-d H:i:s'),
                'appName' => config('app.name', '记账助手')
            ], function ($message) use ($recipientEmail) {
                $message->to($recipientEmail)
                        ->subject('数据库备份失败通知 - ' . config('app.name', '记账助手') . ' - ' . date('Y-m-d'));
            });

        } catch (\Exception $e) {
            Log::error('错误通知邮件发送失败', ['error' => $e->getMessage()]);
        }
    }

    /**
     * 清理旧的备份文件
     */
    private function cleanOldBackups()
    {
        try {
            $backupDir = storage_path('app/backups');
            if (!is_dir($backupDir)) {
                return;
            }

            $files = glob($backupDir . '/money_record_backup_*.sql');

            // 按修改时间排序，保留最新的7个文件
            usort($files, function($a, $b) {
                return filemtime($b) - filemtime($a);
            });

            $filesToDelete = array_slice($files, 7); // 保留最新的7个，删除其余的

            foreach ($filesToDelete as $file) {
                if (unlink($file)) {
                    $this->info('已删除旧备份文件: ' . basename($file));
                }
            }

            if (count($filesToDelete) > 0) {
                Log::info('清理旧备份文件', ['deleted_count' => count($filesToDelete)]);
            }

        } catch (\Exception $e) {
            $this->warn('清理旧备份文件时出错: ' . $e->getMessage());
            Log::warning('清理旧备份文件失败', ['error' => $e->getMessage()]);
        }
    }
}
